﻿namespace NetFx.MSIL
{

  using System;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;

  using System.Reflection;
  using System.Reflection.Emit;
  using System.Runtime.InteropServices;

  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Diagnostics.SymbolStore;

  public enum SignaturePartKind
  {
    Undefined = 0,
    Sentinel  = 1,
    Argument  = 2
  }

  public class SignatruePart
  {
    #region Statics
    public static SignatruePart CreateSentinel()
    {
      return new SignatruePart(SignaturePartKind.Sentinel);
    }
    public static SignatruePart CreateArgument(Type partType)
    {
      return new SignatruePart(partType);
    }
    public static SignatruePart CreateArgument(Type partType, bool isPinned)
    {
      return new SignatruePart(partType,isPinned);
    }
    public static SignatruePart CreateArgument(Type partType, bool isPinned,Type[] requiredCustomModifiers, Type[] optionalCustomModifiers)
    {
      return new SignatruePart(partType,isPinned, requiredCustomModifiers,optionalCustomModifiers);
    }
    #endregion
    #region Fields
    protected SignaturePartKind partKind = SignaturePartKind.Argument;
    protected Type partType = null;
    protected bool isPinned = false;
    protected Type[] requiredCustomModifiers = null;
    protected Type[] optionalCustomModifiers = null;
    #endregion
    #region Properties
    public virtual Type PartType
    {
      get
      {
        return this.partType;
      }
    }
    public virtual SignaturePartKind PartKind
    {
      get
      {
        return this.partKind;
      }
    }
    public virtual bool IsPinned
    {
      get
      {
        return this.isPinned;
      }
    }
    public virtual Type[] RequiredCustomModifiers
    {
      get
      {
        return this.requiredCustomModifiers;
      }
    }
    public virtual Type[] OptionalCustomModifiers
    {
      get
      {
        return this.optionalCustomModifiers;
      }
    }
    #endregion
    #region Constructors
    protected SignatruePart(SignaturePartKind partKind)
    {
      this.partKind = partKind;
    }
    protected SignatruePart(Type partType)
      : this(partType, false)
    {
    }
    protected SignatruePart(Type partType, bool isPinned)
    {
      this.partType = partType;
      this.isPinned = isPinned;
    }
    protected SignatruePart(Type partType, bool isPinned, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers)
    {
      this.partType = partType;
      this.isPinned = isPinned;
      this.requiredCustomModifiers = requiredCustomModifiers;
      this.optionalCustomModifiers = optionalCustomModifiers;
    }
    #endregion
  }

  public class SignatureGenerator:AnyGenerator
  {
    #region Statics
    public static SignatureGenerator CreateFieldSignatureGenerator(ModuleBuilder module)
    {
      return new SignatureGenerator(module,SignatureHelper.GetFieldSigHelper(module));
    }
    //public static SignatureGenerator CreateFieldGenericTypeGenerator(ModuleBuilder module, Type type)
    //{
    //  return new SignatureGenerator(module, SignatureHelper.GetGenericTypeSigToken(module, type));
    //}
    public static SignatureGenerator CreateLocalVariableGenerator(ModuleBuilder module)
    {
      return new SignatureGenerator(module, SignatureHelper.GetLocalVarSigHelper(module));
    }
    public static SignatureGenerator CreateMethodSignatureGenerator(ModuleBuilder module, CallingConvention unmanagedCallConv, Type returnType)
    {
      return new SignatureGenerator(module, SignatureHelper.GetMethodSigHelper(module, unmanagedCallConv, returnType));
    }
    public static SignatureGenerator CreateMethodSignatureGenerator(ModuleBuilder module, CallingConventions callingConvention, Type returnType)
    {
      return new SignatureGenerator(module, SignatureHelper.GetMethodSigHelper(module, callingConvention, returnType));
    }
    public static SignatureGenerator CreateMethodSignatureGenerator(ModuleBuilder module, Type returnType, Type[] parameterTypes)
    {
      return new SignatureGenerator(module, SignatureHelper.GetMethodSigHelper(module, returnType, parameterTypes));
    }
    public static SignatureGenerator CreatePropertySignatureGenerator(ModuleBuilder module, Type returnType, Type[] parameterTypes)
    {
      return new SignatureGenerator(module, SignatureHelper.GetPropertySigHelper(module, returnType, parameterTypes));
    }
    public static SignatureGenerator CreatePropertySignatureGenerator(ModuleBuilder module, Type returnType,
      Type[] requiredReturnTypeCustomModifiers,
      Type[] optionalReturnTypeCustomModifiers,
      Type[] parameterTypes,
      Type[][] requiredParameterTypeCustomModifiers,
      Type[][] optionalParameterTypeCustomModifiers
      )
    {
      return new SignatureGenerator(
        module,
        SignatureHelper.GetPropertySigHelper(module, 
        returnType, 
        requiredReturnTypeCustomModifiers,
        optionalReturnTypeCustomModifiers,
        parameterTypes,
        requiredParameterTypeCustomModifiers,
        optionalParameterTypeCustomModifiers
        ));
    }
    #endregion
    #region Fields
    protected ModuleBuilder moduleBuilder = null;
    protected SignatureHelper signatureHelper = null;
    protected byte[] signature = null;
    protected List<SignatruePart> signatureParts = null;
    #endregion
    #region Properties
    public override object AnyBuilder
    {
      get { return this.signatureHelper; }
    }
    public virtual SignatureHelper SignatureHelper
    {
      get
      {
        return this.signatureHelper;
      }
    }
    public virtual byte[] Signature
    {
      get
      {
        return this.IsSignatureCompleted ? this.signature : this.CompleteSignature();
      }
    }
    public virtual bool IsSignatureCompleted
    {
      get
      {
        return this.signature != null;
      }
    }
    public virtual IList<SignatruePart> SignatureParts
    {
      get
      {
        return this.signatureParts == null ?
          (this.signatureParts= new List<SignatruePart>()).AsReadOnly()
          :this.signatureParts.AsReadOnly();
      }
    }
    public virtual ModuleBuilder ModuleBuilder
    {
      get
      {
        return this.moduleBuilder;
      }
    }
    public virtual SignatureToken SignatureToken
    {
      get
      {
        return this.ModuleBuilder.GetSignatureToken(this.SignatureHelper);
      }
    }
    #endregion
    #region Constructors
    public SignatureGenerator(ModuleBuilder module, SignatureHelper signatureHelper)
    {
      if (module == null) throw new ArgumentNullException("module can not be null!");
      if (signatureHelper == null) throw new ArgumentNullException("signatureHelper can not be null!");
      this.moduleBuilder = module;
      this.signatureHelper = signatureHelper;
    }
    #endregion
    #region Methods
    public virtual SignatruePart AddSentinel()
    {
      SignatruePart sentinel = SignatruePart.CreateSentinel();
      this.SignatureHelper.AddSentinel();
      this.signatureParts.Add(sentinel);
      return sentinel;
    }
    public virtual SignatruePart AddArgument(Type partType)
    {
      SignatruePart argument = SignatruePart.CreateArgument(partType);
      this.SignatureHelper.AddArgument(partType);
      this.signatureParts.Add(argument);
      return argument;
    }
    public virtual SignatruePart AddArgument(Type partType, bool isPinned)
    {
      SignatruePart argument = SignatruePart.CreateArgument(partType,isPinned);
      this.SignatureHelper.AddArgument(partType,isPinned);
      this.signatureParts.Add(argument);
      return argument;
    }
    public virtual SignatruePart AddArgument(Type partType, bool isPinned, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers)
    {
      SignatruePart argument = SignatruePart.CreateArgument(partType,isPinned, requiredCustomModifiers,optionalCustomModifiers);
      this.SignatureHelper.AddArgument(partType,requiredCustomModifiers,optionalCustomModifiers);
      this.signatureParts.Add(argument);
      return argument;
    }
    public virtual SignatruePart[] AddArguments(params SignatruePart[] parts)
    {
      if (parts != null)
      {
        foreach (SignatruePart argument in parts)
        {
          if (argument != null && argument.PartKind == SignaturePartKind.Argument)
          {
            this.AddArgument(argument.PartType, argument.IsPinned, argument.RequiredCustomModifiers, argument.OptionalCustomModifiers);
            this.signatureParts.Add(argument);
          }
        }
      }
      return parts;
    }
    public virtual byte[] CompleteSignature()
    {
      return this.signature = this.SignatureHelper.GetSignature();
    }
    #endregion
    #region Utils
    protected override void Dispose(bool disposing)
    {
      if (disposing)
      {
        this.moduleBuilder = null;
        this.signatureHelper = null;
        this.signature = null;
        if (this.signatureParts != null) this.signatureParts.Clear();
        this.signatureParts = null;
      }
      base.Dispose(disposing);
    }
    #endregion
  }
}
